/*
 * Copyright (c) 2013 WDCY Information Technology Co. Ltd
 * www.wdcy.cc
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * WDCY Information Technology Co. Ltd ("Confidential Information").
 * You shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement you
 * entered into with WDCY Information Technology Co. Ltd.
 */
package org.springframework.web.servlet.view.pdf;

import com.monkey.core.plugin.report.pdf.PdfData;
import com.monkey.utils.MonkeyUtils;
import com.monkey.utils.WebUtils;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.xml.JRXmlLoader;
import org.springframework.context.ApplicationContextException;
import org.springframework.core.io.Resource;
import org.springframework.ui.jasperreports.JasperReportsUtils;
import org.springframework.web.servlet.view.AbstractUrlBasedView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;

/**
 * @author Wu Tianqiang
 */
public class AppPdfView extends AbstractUrlBasedView {

    /**
     * PDF 的数据对象   PdfData 放到model中的key
     */
    public static final String PDF_MAIN_DATA_MODEL_KEY = AppPdfView.class + "-PdfReport";


    public AppPdfView() {
        setContentType("application/pdf");
    }


    private PdfData loadPdfData(Map<String, Object> model) {
        PdfData pdfData = (PdfData) model.get(PDF_MAIN_DATA_MODEL_KEY);
        if (pdfData == null) {
            Set<String> keys = model.keySet();
            for (String key : keys) {
                Object value = model.get(key);
                if (value instanceof PdfData) {
                    pdfData = (PdfData) value;
                }
            }
        }
        return pdfData;
    }


    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        PdfData pdfData = loadPdfData(model);

        String downloadFileName = WebUtils.encodeChineseDownloadFileName(request, pdfData.getDownloadFileName());
        response.setHeader("Content-Disposition", "inline; filename=" + downloadFileName + ";");

        setUrl(pdfData.getReportURL());

        JasperPrint filledReport = fillReport(loadReport(), model);

        renderReport(filledReport, response);
    }

    private JasperReport loadReport() {
        String url = getUrl();
        if (url == null) {
            return null;
        }
        Resource mainReport = getApplicationContext().getResource(url);
        return loadReport(mainReport);
    }

    private JasperReport loadReport(Resource resource) {
        try {
            String filename = resource.getFilename();
            if (filename != null) {
                if (filename.endsWith(".jasper")) {
                    // Load pre-compiled report.
                    if (logger.isInfoEnabled()) {
                        logger.info("Loading pre-compiled Jasper Report from " + resource);
                    }
                    try (InputStream is = resource.getInputStream()) {
                        return (JasperReport) JRLoader.loadObject(is);
                    }
                } else if (filename.endsWith(".jrxml")) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Compiling Jasper Report loaded from " + resource);
                    }
                    try (InputStream is = resource.getInputStream()) {
                        JasperDesign design = JRXmlLoader.load(is);
                        return JasperCompileManager.compileReport(design);
                    }
                }
            }
            throw new IllegalArgumentException(
                    "Report filename [" + filename + "] must end in either .jasper or .jrxml");
        } catch (IOException ex) {
            throw new ApplicationContextException(
                    "Could not load JasperReports report from " + resource, ex);
        } catch (JRException ex) {
            throw new ApplicationContextException(
                    "Could not parse JasperReports report from " + resource, ex);
        }
    }

    @SuppressWarnings("unchecked")
    private JasperPrint fillReport(JasperReport report, Map<String, Object> model) throws JRException {
        JRDataSource jrDataSource = null;
        PdfData pdfData = loadPdfData(model);
        model.putAll(pdfData.getParameters());
        if (MonkeyUtils.isNotEmpty(pdfData)) {
            jrDataSource = JasperReportsUtils.convertReportData(pdfData.getFields());
        }

        if (jrDataSource != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Filling report with JRDataSource [" + jrDataSource + "]");
            }
            return JasperFillManager.fillReport(report, model, jrDataSource);
        } else {
            logger.debug("Filling report with plain model");
            return JasperFillManager.fillReport(report, model);
        }
    }

    private void renderReport(JasperPrint populatedReport, HttpServletResponse response) throws Exception {
        JRExporter exporter = new JRPdfExporter();
        ByteArrayOutputStream baos = createTemporaryOutputStream();
        JasperReportsUtils.render(exporter, populatedReport, baos);
        writeToResponse(response, baos);
    }

    @Override
    protected boolean isUrlRequired() {
        return false;
    }

}